Skip to content

feat: redesign signals list page, restructure manage-signal-sheet, add trace-picker#1387

Open
kolbeyang wants to merge 35 commits intodevfrom
feat/signals-list-redesign
Open

feat: redesign signals list page, restructure manage-signal-sheet, add trace-picker#1387
kolbeyang wants to merge 35 commits intodevfrom
feat/signals-list-redesign

Conversation

@kolbeyang
Copy link
Collaborator

@kolbeyang kolbeyang commented Mar 12, 2026

Summary

  • Redesign signals list page with sparklines and empty state component
  • Restructure manage-signal-sheet from single file into folder with template picker, test panel, schema fields builder, and form fields components
  • Add reusable TracePicker component shared by debugger-sessions sidebar and manage-signal-sheet test panel
  • Add signals/stats API route for sparkline data

Stacked on

This PR stacks on top of feat/signals-visualization and contains the signals list page redesign, manage-signal-sheet restructure, and TracePicker extraction that were split out to reduce the base PR's scope.

Test plan

  • Verify signals list page renders with sparklines
  • Verify create/edit signal sheet works with new folder structure
  • Verify TracePicker works in both debugger-sessions sidebar and signal test panel
  • Verify signal detail page still loads correctly

🤖 Generated with Claude Code


Note

Medium Risk
Adds a new API endpoint and ClickHouse aggregation query for signal activity, plus refactors shared table/trace selection UI; main risk is correctness/performance of the stats query and new client-side fetching/scale switching behavior.

Overview
Signals list page now shows per-signal activity sparklines with a new Activity column, prompt tooltip on the name, a dedicated empty state component, and a scale selector (day/week/month) wired through table meta.

Adds a new GET /api/projects/:projectId/signals/stats endpoint backed by getSignalsStats() (ClickHouse aggregation with WITH FILL) and client-side fetching in signals/index.tsx to populate sparklines.

Extracts a reusable TracePicker (columns + infinite table/search/date filters) and updates debugger session sidebar tabs and the signal test panel to use it; also restructures manage-signal-sheet from a single file into a component folder with separated form, template picker, schema builder, and test workflow.

Written by Cursor Bugbot for commit 8fec11f. This will update automatically on new commits. Configure here.

kolbeyang and others added 19 commits March 11, 2026 11:51
… buckets, FINAL for dedup

- Max height 300px on cluster container, chart fills available height
- Cluster list scrolls when overflow
- Cluster stats query uses WITH FILL to fill empty time buckets with zeros
- Clusters query uses FINAL to deduplicate ReplacingMergeTree rows
- TimeSeriesChart accepts className prop to override chart height

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Breadcrumb uses outer AnimatePresence (positional keys) for level
  add/remove and inner AnimatePresence (node ID keys) for sibling swaps
- Fix unclustered event count using clusteredEventCount from query engine
  instead of summing tree nodes (avoids double-counting from flattening)
- Match skeleton height to loaded state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract ClusterItem into cluster-list/cluster-item.tsx (one component per file)
- Add 500ms framer motion delay before tooltip appears on hover
- Add overflow-hidden to tooltip to prevent text overflow during grow animation
- Use React state for button hover highlight instead of CSS :hover to handle portal overlay

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…eusable ClusterItem

- Convert event detail panel from inline sidebar to animated full-screen drawer
- Add selectedEvent/setSelectedEvent to signal store for shared state
- Fix cluster stats to use executeQuery (query engine) instead of direct clickhouseClient,
  which was querying raw signal_events table missing the clusters column
- Fix unclustered stats to query empty(clusters) directly instead of subtraction
  (subtraction overcounted due to events belonging to multiple clusters)
- Use clusters array column on signal_events (via query engine view) for event filtering
- Make ClusterItem reusable with iconVariant/color props, use for unclustered button
- Always show filtered vs total event counts in cluster tooltips when time range active
- Add clusters to query engine allowed columns for signal_events

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove console.log debug statements from events-table and events action
- Add cancellation flag to stats-fetching useEffect to prevent stale data
- Stabilize filter array reference with useMemo to prevent infinite re-fetches
- Fix unsafe EventCluster-to-ClusterNode casts with proper typing
- Add timeout cleanup on unmount in ClusterItem hover tooltip
- Use stable node.id keys instead of index-based keys in breadcrumb animation
- Fix breadcrumb reset to depend on clusterPath content, not just length
- Validate scale param in signals stats route instead of bare type assertion
- Fix duplicate fuchsia-500 color in palette, replace with violet-500
- Remove unused overlayRef and redundant XIcon import
- Fix missing space in cluster-stacked-chart import
- Remove stale TODO comments

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ehavior

- Use nuqs useClusterId hook as single source of truth for cluster navigation
- Parameterize store selectors as factory functions accepting clusterId
- Handle leaf node and unclustered selection: chart shows selected data,
  list stays at parent level, deselect returns to parent
- Build color map in parent so chart colors match list colors
- Rename clusters-table to clusters-section
- Use ResizablePanelGroup for cluster list/chart layout
- Trace ID column: show tail with RTL truncation, use ArrowUpRight icon

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… and Button

- Split manage-signal-sheet.tsx into folder with one component per file
- Group template-picker and test-results-view into subfolders with index.tsx pattern
- Add xs/sm/md size prop to Input component (xs default, preserves existing behavior)
- Add md size variant to Button component
- Use size sm for signal name input, md for Test/Create/Run test buttons
- Right-justify footer buttons, disable Test when Create is disabled
- Reduce sheet width by 10%, remove selected trace chip, remove test flask icon
- Rename template label to "Template" with consistent header styling

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d trace-picker

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… prevention

- Store clusterTree as state, computed once when rawClusters changes,
  so selectTree returns stable references for Zustand equality checks
- Replace no-op cancelled variable with AbortController pattern to
  cancel in-flight cluster stats requests on dependency changes
- Rethrow AbortError past per-fetch .catch() so it propagates correctly

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace tab bar with a breadcrumb dropdown that opens a 3-column
overview popover (Input → Definition → Output). Move "Edit Signal"
to the far right, opening the sheet directly. Support controlled
mode in ManageSignalSheet by conditionally rendering SheetTrigger.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ClickHouse may return count() as strings; wrap with Number() to ensure
correct strict equality checks. Also extract individual search params
(pastHours, startDate, endDate) so the stats-fetch effect only re-fires
when those values change, not on every unrelated URL param update.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…se columns

- Replace signal header popover/dropdown with tooltip overlay on TabsList
- Remove TracePicker dual-mode (url/state), keep only state-based filtering
- Reuse trace-picker columns in debugger sidebar instead of duplicate columns.tsx
- Restructure test-panel and test-results-view into folders
- Fix filter memoization key: use JSON.stringify instead of join(",")
- Rename "All Events" breadcrumb to "Event clusters"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…nal-sheet restructure

Reverts signals list page, manage-signal-sheet folder restructure,
signal-sparkline, empty-row, trace-picker, and debugger-sessions
sidebar trace-picker usage back to dev versions to reduce PR scope.
These changes will be stacked in a follow-up PR.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… query

Move cluster event counting from events/stats.ts into lib/actions/clusters
and return both clustered and unclustered counts in a single API call,
eliminating the need for separate requests and clusterIds filtering.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kolbeyang kolbeyang requested a review from olzhik11 March 12, 2026 18:43
@kolbeyang kolbeyang self-assigned this Mar 12, 2026
const id = watch("id");

return (
<Button type="submit" size="md" disabled={isLoading || !isValid} handleEnter>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@kolbeyang let's remove handleEnter from here, this logic was written long time ago and does not behave well, we will remove it entirely later

@olzhik11
Copy link
Member

lgtm, i will review once more as we merge feat/signals-visualization

kolbeyang and others added 4 commits March 13, 2026 08:22
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ckHouse queries

Adds an optional `dataType` discriminator to the filter schema so consumers
(especially the events defaultProcessor) can use the correct ClickHouse
extraction function for payload fields (e.g. simpleJSONExtractFloat for
numbers, simpleJSONExtractBool for booleans). The field is optional to
avoid breaking persisted triggers or bookmarked URL params.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
ClickHouse returns count() as JSON numbers (output_format_json_quote_64bit_integers=0),
so type them as number directly instead of string + parseInt mapping.

Also minor tooltip styling tweaks.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix renamed store exports (selectBreadcrumb → getBreadcrumb, etc.)
- Type cluster integer fields (level, numChildrenClusters, numEvents)
  as number, removing redundant parseInt wrappers

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kolbeyang kolbeyang force-pushed the feat/signals-list-redesign branch from e9e8139 to 12b7e0c Compare March 13, 2026 16:43
kolbeyang and others added 2 commits March 13, 2026 09:45
…d EventsStatsDataPoint import)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kolbeyang kolbeyang force-pushed the feat/signals-list-redesign branch from 12b7e0c to 808940f Compare March 13, 2026 16:47
Prevents unnecessary event refetches and re-renders caused by new
references from getFilterClusterIds and getFilteredCountByCluster.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kolbeyang kolbeyang force-pushed the feat/signals-list-redesign branch from 399e62f to 4361608 Compare March 13, 2026 17:07
Copy link

@cursor cursor bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 1 potential issue.

Fix All in Cursor

Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.

meta: {
customDropdownItems: (table: unknown) => {
const meta = (table as { options: { meta: SignalTableMeta } }).options.meta;
const scale = meta?.sparklineScale ?? "day";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inconsistent sparkline scale fallback values

Medium Severity

The sparklineScale fallback in customDropdownItems defaults to "day", while the header function defaults to "week", and the initial state in the parent component is also "week". This means the dropdown's active/check indicator will highlight the wrong scale item ("day" instead of "week") when meta is briefly undefined during initialization — creating a visual mismatch between the header label showing "(week)" and the dropdown highlighting "Day".

Additional Locations (1)
Fix in Cursor Fix in Web

kolbeyang and others added 2 commits March 13, 2026 10:13
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kolbeyang kolbeyang force-pushed the feat/signals-list-redesign branch from 4361608 to b2dcc0f Compare March 13, 2026 17:16
kolbeyang and others added 5 commits March 13, 2026 10:19
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…d trace-picker

- Redesign signals list page with sparklines and empty state component
- Restructure manage-signal-sheet from single file to folder with
  template picker, test panel, schema fields builder, and form fields
- Add reusable TracePicker component used by debugger-sessions sidebar
  and manage-signal-sheet test panel
- Add signals/stats API route and lib for sparkline data
- Update signal detail page to use new manage-signal-sheet folder import

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…t cleanup

- Replace fragile client-side timestamp formatting/matching with
  ClickHouse WITH FILL clause for server-side zero-filling
- Add AbortController to sparkline fetch useEffect to prevent
  race conditions from stale responses

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ry refetches

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@kolbeyang kolbeyang force-pushed the feat/signals-list-redesign branch from 67151af to 8fec11f Compare March 13, 2026 17:19
Base automatically changed from feat/signals-visualization to dev March 13, 2026 18:43
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants